with Ada.Calendar;
with Ada.Exceptions;

with process_line; use process_line;
with GUI.Instances;

with ffxilog_interface;       use ffxilog_interface;
with ffxilog_interface.track; use ffxilog_interface.track;

with win32api_interface.enum_windows;
with win32api_interface.memory.helper;
with win32api_interface.process.helper;

separate(ffxialarm.tasks)
package body Logger_Routines is
  procedure Start(Process : in Process_ID;
                  par     : out Parameters;
                  success : out Boolean) is
    use GUI;
    use GUI.Instances;
    use GUI.Log;
    use win32api_interface.process;
    use win32api_interface.process.helper;
    use win32api_interface.enum_windows;
    Opened    : Boolean;
    --Process   : Process_ID;
    Base_addr : win32api_interface.Address;
  begin
    Success := False;
    Opened := False;
    Set_Status(Main_Window, "Finding process...");
    --declare
    --  instances : constant result_info_array :=
    --    Enumerate_Windows(Class => "FFXiClass", pName => "pol.exe");
    --  n : Integer;
    --begin
      --if instances'length = 0 then
      --  Ada.Exceptions.Raise_Exception(No_FFXI'Identity,
      --    "No running Final Fantasy XI instance has been found.");
      --elsif instances'length = 1 then
      --  Process := instances(1).hProc;
      --else
     --   Choose_Instance(Main_Window, instances, n);
     --   if n = 0 then
    --      raise No_Select;
      --  else
      --    Process := instances(n).hProc;
     --   end if;
      --end if;
    --end;
    Set_Status(Main_Window, "Accessing process...");
    par.Access_Handle := Open(Process);
    Opened := True;
    Set_Status(Main_Window, "Finding module...");
    --Base_addr := Get_Module_Base_Address("FFIX.dll", Process);
    Base_addr := Get_Module_Base_Address("FFXiMain.dll", Process);
    Set_Status(Main_Window, "Accessing Log...");
    par.Log_Info := Get_Pointer_To_Log_Info(par.Access_Handle, Base_addr);
    declare
      temp_Log : string := Get_Log(par.Access_Handle, par.Log_Info);
    begin
      null;
    end;
    par.checked_lines := Get_Number_of_lines(par.Access_Handle, par.Log_Info);
    Log_Event(Log_Window, False, Ada.Calendar.Clock,
              Program, "Logging started.");
    Set_Status(Main_Window, "Started, waiting for incoming text...");
    Set_Stop(Main_Window, False);
    success := True;
  exception
    --when No_Select => Set_Status(Main_Window, "Idle");
    when E : others =>
      if Opened then Close(par.Access_Handle); end if;
      Set_Status(Main_Window, "Error during initialization");
      Message_Box
        (Main_Window,
         "Error during initialization: "&
         Ada.Exceptions.Exception_Name (E)&": "&
         Ada.Exceptions.Exception_Message (E));
  end Start;

  procedure Work(par     : in out Parameters;
                 success : out Boolean) is
    use GUI;
    use GUI.Log;
    use win32api_interface.memory.helper;
    use win32api_interface.process.helper;
    i : integer;
  begin
    Success := False;
    declare
      current_lines : constant integer :=
        Get_Number_of_Lines(par.Access_Handle, par.Log_Info);

      procedure Analyze_Archived_Log is
        Archived_Log : constant string :=
          Get_Archived_Log(par.Access_Handle, par.Log_Info, par.checked_lines);

        Missed_Archive_Lines : constant Integer := 50-par.checked_lines;
        Alarm : Integer;
      begin
        for j in 1..Missed_Archive_Lines loop
          process(Main_Window, Log_Window, Get_Line(Archived_Log, j), Alarm);
          Play(Alarm);
        end loop;
      end Analyze_Archived_Log;

      procedure Analyze_Current_Log is
        Current_Log : constant string :=
          Get_Log(par.Access_Handle, par.Log_Info);
        Alarm : Integer;
      begin
        while current_lines > par.checked_lines+i loop
          process(Main_Window, Log_Window,
                  Get_Line(Current_Log, par.checked_lines+i+1), Alarm);
          Play(Alarm);
          i:= i+1;
        end loop;
      end Analyze_Current_Log;
    begin
      i := 0;
      if There_are_Lines_on_Current_Log(current_lines) then
        if Log_was_Archived(current_lines, par.checked_lines) then
          if Not_All_Lines_from_Archived_Log_were_read(par.checked_lines) then
            Analyze_Archived_Log;
          end if;
          par.checked_lines := 0;
        end if;
        Analyze_Current_Log;
      else
        par.checked_lines := current_lines;
      end if;
      par.checked_lines := par.checked_lines+i;
    end;
    Success := True;
  exception
    when Read_Bytes_Error =>
      declare
      begin
        Log_Event(Log_Window, False, Ada.Calendar.Clock,
                  Program, "Parsing Error");
        par.checked_lines :=
          Get_Number_of_lines(par.Access_Handle, par.Log_Info);
      exception
        when Read_Bytes_Error =>
          Close(par.Access_Handle);
          Stop_Logger;
          Set_Stop(Main_window, True);
          Log_Event(Log_Window, False, Ada.Calendar.Clock,
                    Program, "Access error, logging stopped.");
          Set_Status(Main_Window, "Access error, logging stopped");
      end;
    when E : others =>
      Close(par.Access_Handle);
      Stop_Logger;
      Set_Stop(Main_window, True);
      Log_Event(Log_Window, False, Ada.Calendar.Clock,
                  Program, "Access error, logging stopped.");
      Set_Status(Main_Window, "Unhandled error while logging, stopped");
      Message_Box
        (Main_Window, Ada.Exceptions.Exception_Name (E)&": "&
         Ada.Exceptions.Exception_Message (E));
  end Work;

  procedure Finish(par : in Parameters) is
    use GUI;
    use GUI.Log;
    use win32api_interface.process.helper;
  begin
    Close(par.Access_Handle);
    Set_Stop(Main_window, True);
    Log_Event(Log_Window, False, Ada.Calendar.Clock,
              Program, "Logging stopped by the user.");
    Set_Status(Main_Window, "Idle");
  end Finish;
end Logger_Routines;
